package it.polimi.jita.cp.block.dd;

import it.polimi.jita.cp.block.InputBuilder;
import it.polimi.jita.cp.block.de.DemandEstimatorInputBuilder.WorldState;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONObject;

public class DynamicDecisionMakerInputBuilder extends
		InputBuilder<DynamicDecisionMakerInput> {

	private Map<WorldState, Integer> worldStateIndex = new HashMap<>();

	public DynamicDecisionMakerInputBuilder() {
		WorldState[] worldStates = WorldState.values();
		for (WorldState worldState : worldStates) {
			worldStateIndex.put(worldState, worldState.ordinal());
		}
	}

	@Override
	protected DynamicDecisionMakerInput createInput() throws Exception {

		String filePath = getOptionValue("-i");
		URL url = this.getClass().getClassLoader().getResource(filePath);
		InputStream in = url.openStream();
		String sourceStr = IOUtils.toString(in, "UTF-8");
		JSONObject root = new JSONObject(sourceStr);

		Set<VirtualMachineData> vms = new HashSet<>();
		Integer samplingTime = Integer.parseInt(getOptionValue("-s"));

		JSONArray vmsJsonArray = root.getJSONArray("vms");
		for (int i = 0; i < vmsJsonArray.length(); i++) {
			JSONObject vmJsonObj = vmsJsonArray.getJSONObject(i);

			String name = vmJsonObj.getString("name");
			Integer vCpuNum = vmJsonObj.getInt("vCpuNum");
			Long intantsInThisState = vmJsonObj.getLong("intantsInThisState");
			List<Instant> instants = generateInstantFromLog(
					vmJsonObj.getString("log"), samplingTime, vCpuNum);
			VirtualMachineData vm = new VirtualMachineData(name, vCpuNum,
					intantsInThisState, instants);
			vms.add(vm);
		}

		JSONObject newVmJsonOBj = root.getJSONObject("newVm");
		NewVirtualMachineData newVm = new NewVirtualMachineData(
				newVmJsonOBj.getString("name"), newVmJsonOBj.getInt("vCpuNum"));

		HostData hostData = new HostData(vms, root.getInt("pCpuNum"));

		return new DynamicDecisionMakerInput(hostData, newVm, samplingTime);
	}

	private List<Instant> generateInstantFromLog(String filePath,
			Integer samplingInt, Integer vCpuNum) throws IOException {
		List<Instant> instants = new ArrayList<>();

		// TODO Log open file
		System.out.println(String.format("Opening file: %s", filePath));

		URL url = this.getClass().getClassLoader().getResource(filePath);
		InputStream in = url.openStream();
		InputStreamReader isr = new InputStreamReader(in);
		CSVParser csvParser = new CSVParser(isr, CSVFormat.DEFAULT);
		System.out.println(String.format("Reading file: %s", filePath));

		boolean needToCheckHeader = false;
		if (getCl().hasOption("-h")) {
			needToCheckHeader = true;
		}

		for (Iterator<CSVRecord> iterator = csvParser.iterator(); iterator
				.hasNext();) {
			CSVRecord csvRecord = iterator.next();
			if (needToCheckHeader) {
				for (int i = 0; i < csvRecord.size(); i++) {
					try {
						WorldState ws = WorldState.valueOf(csvRecord.get(i)
								.toUpperCase());
						worldStateIndex.put(ws, i);
					} catch (Exception e) {
						System.out.println(String.format(
								"Word '%s' is not recognized as WorldState",
								csvRecord.get(i)));
					}
				}

				needToCheckHeader = false;

			} else {
				Double runDbl = Double.parseDouble(csvRecord
						.get(worldStateIndex.get(WorldState.RUN)));
				Double blockDbl = Double.parseDouble(csvRecord
						.get(worldStateIndex.get(WorldState.BLOCK)));
				Double waitDbl = Double.parseDouble(csvRecord
						.get(worldStateIndex.get(WorldState.WAIT)));

				Double block = blockDbl / samplingInt / vCpuNum;
				Double wait = waitDbl / samplingInt / vCpuNum;
				Double run = runDbl / samplingInt / vCpuNum;
				Instant instant = new Instant(block, wait, run);
				instants.add(instant);
			}
		}
		csvParser.close();
		return instants;
	}

}
